package io.kwdb.sdk;

import io.grpc.StatusRuntimeException;

/** Represents the status of an operation in KWDB SDK. Aligned with C++ SDK Status class. */
public class Status {

  public enum Code {
    OK(0),
    INVALID_ARGUMENT(1),
    NOT_FOUND(2),
    ALREADY_EXISTS(3),
    PERMISSION_DENIED(4),
    RESOURCE_EXHAUSTED(5),
    ABORTED(6),
    OUT_OF_RANGE(7),
    UNIMPLEMENTED(8),
    INTERNAL(9),
    UNAVAILABLE(10),
    DATA_LOSS(11),
    UNAUTHENTICATED(12),
    FAILED_PRECONDITION(13),
    DEADLINE_EXCEEDED(14),
    UNKNOWN(15);

    private final int value;

    Code(int value) {
      this.value = value;
    }

    public int getValue() {
      return value;
    }

    public static Code fromValue(int value) {
      for (Code code : values()) {
        if (code.value == value) {
          return code;
        }
      }
      return UNKNOWN;
    }
  }

  private final Code code;
  private final String message;

  private Status(Code code, String message) {
    this.code = code;
    this.message = message;
  }

  public Code getCode() {
    return code;
  }

  public String getMessage() {
    return message;
  }

  public boolean isOk() {
    return code == Code.OK;
  }

  public boolean isError() {
    return code != Code.OK;
  }

  // Static factory methods
  public static Status ok() {
    return new Status(Code.OK, "");
  }

  public static Status ok(String message) {
    return new Status(Code.OK, message);
  }

  public static Status error(Code code, String message) {
    return new Status(code, message);
  }

  public static Status invalidArgument(String message) {
    return new Status(Code.INVALID_ARGUMENT, message);
  }

  public static Status notFound(String message) {
    return new Status(Code.NOT_FOUND, message);
  }

  public static Status alreadyExists(String message) {
    return new Status(Code.ALREADY_EXISTS, message);
  }

  public static Status permissionDenied(String message) {
    return new Status(Code.PERMISSION_DENIED, message);
  }

  public static Status resourceExhausted(String message) {
    return new Status(Code.RESOURCE_EXHAUSTED, message);
  }

  public static Status aborted(String message) {
    return new Status(Code.ABORTED, message);
  }

  public static Status internal(String message) {
    return new Status(Code.INTERNAL, message);
  }

  public static Status unavailable(String message) {
    return new Status(Code.UNAVAILABLE, message);
  }

  public static Status deadlineExceeded(String message) {
    return new Status(Code.DEADLINE_EXCEEDED, message);
  }

  public static Status unknown(String message) {
    return new Status(Code.UNKNOWN, message);
  }

  public static Status unimplemented(String message) {
    return new Status(Code.UNIMPLEMENTED, message);
  }

  public static Status cancelled(String message) {
    return new Status(Code.ABORTED, message);
  }

  public static Status failed(String message) {
    return new Status(Code.INTERNAL, message);
  }

  /** Convert from gRPC status to SDK status */
  public static Status fromGrpc(StatusRuntimeException e) {
    Code code;
    switch (e.getStatus().getCode()) {
      case OK:
        code = Code.OK;
        break;
      case INVALID_ARGUMENT:
        code = Code.INVALID_ARGUMENT;
        break;
      case NOT_FOUND:
        code = Code.NOT_FOUND;
        break;
      case ALREADY_EXISTS:
        code = Code.ALREADY_EXISTS;
        break;
      case PERMISSION_DENIED:
        code = Code.PERMISSION_DENIED;
        break;
      case RESOURCE_EXHAUSTED:
        code = Code.RESOURCE_EXHAUSTED;
        break;
      case ABORTED:
        code = Code.ABORTED;
        break;
      case OUT_OF_RANGE:
        code = Code.OUT_OF_RANGE;
        break;
      case UNIMPLEMENTED:
        code = Code.UNIMPLEMENTED;
        break;
      case INTERNAL:
        code = Code.INTERNAL;
        break;
      case UNAVAILABLE:
        code = Code.UNAVAILABLE;
        break;
      case DATA_LOSS:
        code = Code.DATA_LOSS;
        break;
      case UNAUTHENTICATED:
        code = Code.UNAUTHENTICATED;
        break;
      case FAILED_PRECONDITION:
        code = Code.FAILED_PRECONDITION;
        break;
      case DEADLINE_EXCEEDED:
        code = Code.DEADLINE_EXCEEDED;
        break;
      case UNKNOWN:
      default:
        code = Code.UNKNOWN;
        break;
    }
    return new Status(
        code,
        e.getStatus().getDescription() != null ? e.getStatus().getDescription() : e.getMessage());
  }

  @Override
  public String toString() {
    if (isOk()) {
      return "Status{OK}";
    }
    return "Status{" + "code=" + code + ", message='" + message + '\'' + '}';
  }
}
